package com.appdynamics.monitors.hadoop.communicator; import com.appdynamics.extensions.NumberUtils; import com.appdynamics.extensions.StringUtils; import com.appdynamics.extensions.conf.MonitorConfiguration; import com.appdynamics.extensions.http.HttpClientUtils; import com.appdynamics.extensions.http.UrlBuilder; import com.appdynamics.extensions.util.GroupCounter; import com.appdynamics.extensions.util.JsonUtils; import com.appdynamics.extensions.util.MetricUtils; import com.appdynamics.extensions.util.YmlUtils; import com.appdynamics.monitors.hadoop.input.Metric; import com.appdynamics.monitors.hadoop.input.MetricConfig; import com.appdynamics.monitors.hadoop.input.Stat; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.node.ArrayNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.math.BigDecimal; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; /** * Created by abey.tom on 9/22/16. */ public class ResourceMgrMetricsFetcherTask implements Runnable { public static final String[] KNOWN_STATUS = {"UNDEFINED", "SUCCEEDED", "FAILED", "KILLED"}; public static final String[] KNOWN_STATES = {"NEW", "NEW_SAVING", "SUBMITTED", "ACCEPTED", "RUNNING", "FINISHED", "FAILED", "KILLED"}; public static final Logger logger = LoggerFactory.getLogger(ResourceMgrMetricsFetcherTask.class); private final MonitorConfiguration configuration; private final Map<String, ?> server; public ResourceMgrMetricsFetcherTask(MonitorConfiguration configuration, Map<String, ?> server) { this.configuration = configuration; this.server = server; } public void run() { try { runTask(); } catch (Exception e) { logger.error("Exception while running the task", e); } } private void runTask() { MetricConfig metricConfig = (MetricConfig) configuration.getMetricsXmlConfiguration(); Stat[] stats = metricConfig.getStats(); if (stats != null && stats.length > 0) { for (Stat stat : stats) { String urlPath = stat.getUrl(); if (urlPath != null) { String fullUrl = UrlBuilder.fromYmlServerConfig(server) .path(urlPath).build(); JsonNode response = getResponseAsJson(fullUrl); if (response != null) { parseResponse(stat, response); } else { logger.error("The url [{}] returned no data ", fullUrl); } } } } printSchedulerMetrics(); printJobTypeMonitoring(); } protected JsonNode getResponseAsJson(String fullUrl) { return HttpClientUtils.getResponseAsJson(configuration.getHttpClient() , fullUrl, JsonNode.class, Collections.singletonMap("Accept", "application/json")); } protected void printJobTypeMonitoring() { Map<String, ?> config = configuration.getConfigYml(); int monitoringTimePeriod = YmlUtils.getInt(config.get("monitoringTimePeriod"), 15); long monitoringPeriod = System.currentTimeMillis() - monitoringTimePeriod * 60 * 1000; List<Map<String, ?>> applications = (List<Map<String, ?>>) config.get("applications"); if (applications != null) { for (Map<String, ?> application : applications) { String applicationType = (String) application.get("type"); String prefix = StringUtils.concatMetricPath(getMetricPrefix(), "Current Apps", applicationType); JsonNode response = getAppsOfTypeResponse(monitoringPeriod, application); JsonNode apps = JsonUtils.getNestedObject(response, "apps", "app"); List<String> names = (List<String>) application.get("names"); if (apps instanceof ArrayNode) { ArrayNode jsonNodes = (ArrayNode) apps; GroupCounter<String> stateCounter = new GroupCounter<String>(); GroupCounter<String> statusCounter = new GroupCounter<String>(); for (JsonNode jsonNode : jsonNodes) { String name = JsonUtils.getTextValue(jsonNode, "name"); if (matches(name, names)) { String state = JsonUtils.getTextValue(jsonNode, "state"); stateCounter.increment(state); String status = JsonUtils.getTextValue(jsonNode, "finalStatus"); statusCounter.increment(status); } else { logger.debug("The application name [{}] didnt match with the filter {}", name, names); } } printAppTypeStates(StringUtils.concatMetricPath(prefix, "Final Status"), statusCounter, KNOWN_STATUS); printAppTypeStates(StringUtils.concatMetricPath(prefix, "State"), stateCounter, KNOWN_STATES); } else { logger.warn("There are no jobs of type {} found between [{}] and now", applicationType, new Date(monitoringPeriod)); } } } } private String getMetricPrefix() { return StringUtils.concatMetricPath(configuration.getMetricPrefix(), (String) server.get("name")); } private boolean matches(String name, List<String> regexes) { if (regexes != null) { for (String regex : regexes) { if (name.matches(regex)) { return true; } } } else { return true; } return false; } private void printAppTypeStates(String prefix, GroupCounter<String> statusCounter, String[] constants) { for (String stat : constants) { Long value = statusCounter.get(stat); BigDecimal val; if (value == null) { val = new BigDecimal("0"); } else { val = new BigDecimal(value); } String metricPath = StringUtils.concatMetricPath(prefix, stat); configuration.getMetricWriter().printMetric(metricPath, val, "OBS.CUR.COL"); } } /** * http://192.168.1.216:8088/ws/v1/cluster/apps?monitoringTimePeriod=15&applicationTypes=MAPREDUCE * * @param monitoringPeriod * @param application */ private JsonNode getAppsOfTypeResponse(long monitoringPeriod, Map<String, ?> application) { String type = (String) application.get("type"); UrlBuilder path = UrlBuilder.fromYmlServerConfig(server) .path("/ws/v1/cluster/apps") .query("applicationTypes", type) .query("startedTimeBegin", String.valueOf(monitoringPeriod)); return getResponseAsJson(path.build()); } private void printSchedulerMetrics() { JsonNode response = getSchedulerResponse(); if (response != null) { JsonNode json = JsonUtils.getNestedObject(response, "scheduler", "schedulerInfo"); String schedulerType = JsonUtils.getTextValue(json, "type"); logger.debug("The scheduler type is {}", schedulerType); if ("capacityScheduler".equals(schedulerType)) { String label = "Capacity Scheduler|Queues"; Stat stat = getStatsByName("capacitySchedulerQueue"); printCapacitySchedulerQueueMetrics(json, stat, label); } else if ("fifoScheduler".equals(schedulerType)) { String label = "FIFO Scheduler|Queues"; Stat stat = getStatsByName("fifoScheduler"); stat.setLabel(label); parseResponse(stat, json); } else if ("fairScheduler".equals(schedulerType)) { JsonNode rootQueue = json.get("rootQueue"); if (rootQueue != null) { String label = "Fair Scheduler|Queues"; Stat stat = getStatsByName("fairSchedulerQueue"); printFairSchedulerQueueMetrics(rootQueue, stat, label); } } else { logger.error("Unknown scheduler type {} from teh response {}", schedulerType, json); } } } protected JsonNode getSchedulerResponse() { UrlBuilder path = UrlBuilder.fromYmlServerConfig(server).path("/ws/v1/cluster/scheduler"); return getResponseAsJson(path.build()); } private void printCapacitySchedulerQueueMetrics(JsonNode parentQueue, Stat stat, String label) { stat.setLabel(label); parseResponse(stat, parentQueue); JsonNode childQueues = JsonUtils.getNestedObject(parentQueue, "queues", "queue"); if (childQueues instanceof ArrayNode) { ArrayNode queues = (ArrayNode) childQueues; for (JsonNode childQueue : queues) { String qName = JsonUtils.getTextValue(parentQueue, "queueName"); printCapacitySchedulerQueueMetrics(childQueue, stat, StringUtils.concatMetricPath(label, qName, "Queues")); } } else if (childQueues != null) { logger.warn("Expecting an Array. However found {}", childQueues); } } private void printFairSchedulerQueueMetrics(JsonNode parentQueue, Stat stat, String label) { stat.setLabel(label); parseResponse(stat, parentQueue); JsonNode childQueues = parentQueue.get("childQueues"); if (childQueues instanceof ArrayNode) { ArrayNode queues = (ArrayNode) childQueues; for (JsonNode childQueue : queues) { String qName = JsonUtils.getTextValue(parentQueue, "queueName"); printFairSchedulerQueueMetrics(childQueue, stat, StringUtils.concatMetricPath(label, qName, "Queues")); } } else if (childQueues != null) { logger.warn("Expecting an Array. However found {}", childQueues); } } private Stat getStatsByName(String name) { MetricConfig config = (MetricConfig) configuration.getMetricsXmlConfiguration(); Stat[] stats = config.getStats(); if (stats != null) { for (Stat stat : stats) { if (name != null && name.equals(stat.getName())) { return stat; } } } return null; } private void parseResponse(Stat stat, JsonNode response) { JsonNode children; if (stat.getChildren() != null) { String[] split = stat.getChildren().split("\\|"); children = JsonUtils.getNestedObject(response, split); } else { children = response; } if (children instanceof ArrayNode) { ArrayNode nodes = (ArrayNode) children; for (JsonNode node : nodes) { reportMetrics(node, stat); } } else if (children instanceof JsonNode) { reportMetrics(children, stat); } } private void reportMetrics(JsonNode node, Stat stat) { String statPrefix = StringUtils.concatMetricPath(getMetricPrefix(), stat.getLabel()); Metric[] metrics = stat.getMetrics(); if (metrics != null) { for (Metric metric : metrics) { String attr = metric.getAttr(); String metricValue = JsonUtils.getTextValue(node, attr.split("\\|")); MetricConfig config = (MetricConfig) configuration.getMetricsXmlConfiguration(); metricValue = metric.convertValue(attr, metricValue, config); if (NumberUtils.isNumber(metricValue)) { BigDecimal val = MetricUtils.multiplyAndRound(metricValue, metric.getMultiplier()); String label = getLabel(metric, node); if (label != null) { String metricPath = StringUtils.concatMetricPath(statPrefix, label); configuration.getMetricWriter().printMetric(metricPath, val, getMetricType(stat, metric)); } } else { logger.debug("The metric path {} didnt return any value", attr); } } } } private String getMetricType(Stat stat, Metric metric) { if (metric.getMetricType() != null) { return metric.getMetricType(); } else if (stat.getMetricType() != null) { return stat.getMetricType(); } else { return "AVG.AVG.COL"; } } private String getLabel(Metric metric, JsonNode node) { String label = metric.getLabel(); if (label != null) { String[] split = label.split("\\|"); StringBuilder sb = new StringBuilder(); for (int i = 0; i < split.length; i++) { String segment = split[i]; if (segment.startsWith("{") && segment.endsWith("}")) { String variable = segment.substring(1, segment.length() - 1); String textValue = JsonUtils.getTextValue(node, variable); if (textValue != null) { textValue = textValue.replace(":", "-"); sb.append(textValue).append("|"); } else { logger.warn("Cannot resolve the value of dynamic path {} from label {}", segment, label); return null; } } else { sb.append(segment).append("|"); } } if (sb.length() > 0) { sb.deleteCharAt(sb.length() - 1); } return sb.toString(); } else { return metric.getAttr(); } } }